// Properties (属性) 示例
// 演示 Pascal 中属性的各种用法
program p_33_properties;

{$mode objfpc}{$H+}
// {$mode objfpc} 启用 Object Pascal 模式，支持面向对象编程
// {$H+} 启用长字符串支持（AnsiString）

// 引入单元文件
uses
  SysUtils;  // 系统工具单元，包含常用的系统函数

// 定义一个简单的人员类，演示基本属性用法
type
  TPerson = class
  private
    FName: String;
    FAge: Integer;
    FEmail: String;
    
    // 属性访问方法
    function GetName: String;
    procedure SetName(const Value: String);
    function GetAge: Integer;
    procedure SetAge(Value: Integer);
    function GetEmail: String;
    procedure SetEmail(const Value: String);
    
  public
    constructor Create(const AName: String; AAge: Integer; const AEmail: String);
    
    // 只读属性（只有读取方法）
    function GetID: String;
    
    // 属性声明
    property Name: String read GetName write SetName;
    property Age: Integer read GetAge write SetAge;
    property Email: String read GetEmail write SetEmail;
    property ID: String read GetID;  // 只读属性
  end;

// TPerson类的实现
constructor TPerson.Create(const AName: String; AAge: Integer; const AEmail: String);
begin
  inherited Create;
  FName := AName;
  FAge := AAge;
  FEmail := AEmail;
  WriteLn('  创建人员: ', FName);
end;

function TPerson.GetName: String;
begin
  WriteLn('    读取姓名属性');
  Result := FName;
end;

procedure TPerson.SetName(const Value: String);
begin
  WriteLn('    设置姓名属性为: ', Value);
  if Value <> '' then
    FName := Value
  else
    FName := '未知';
end;

function TPerson.GetAge: Integer;
begin
  WriteLn('    读取年龄属性');
  Result := FAge;
end;

procedure TPerson.SetAge(Value: Integer);
begin
  WriteLn('    设置年龄属性为: ', Value);
  if (Value >= 0) and (Value <= 150) then
    FAge := Value
  else
    FAge := 0;
end;

function TPerson.GetEmail: String;
begin
  WriteLn('    读取邮箱属性');
  Result := FEmail;
end;

procedure TPerson.SetEmail(const Value: String);
begin
  WriteLn('    设置邮箱属性为: ', Value);
  // 简单的邮箱验证
  if (Pos('@', Value) > 0) and (Pos('.', Value) > 0) then
    FEmail := Value
  else
    FEmail := '无效邮箱';
end;

function TPerson.GetID: String;
begin
  // 生成一个简单的ID（实际应用中可能更复杂）
  Result := 'ID_' + IntToStr(Integer(Self)) + '_' + FName;
end;

// 定义一个矩形类，演示计算属性
type
  TRectangle = class
  private
    FWidth: Real;
    FHeight: Real;
    
    // 计算属性的访问方法
    function GetArea: Real;
    function GetPerimeter: Real;
    function GetIsSquare: Boolean;
    
  public
    constructor Create(AWidth, AHeight: Real);
    
    // 属性声明
    property Width: Real read FWidth write FWidth;
    property Height: Real read FHeight write FHeight;
    property Area: Real read GetArea;                // 只读计算属性
    property Perimeter: Real read GetPerimeter;      // 只读计算属性
    property IsSquare: Boolean read GetIsSquare;     // 只读计算属性
  end;

// TRectangle类的实现
constructor TRectangle.Create(AWidth, AHeight: Real);
begin
  inherited Create;
  FWidth := AWidth;
  FHeight := AHeight;
  WriteLn('  创建矩形: ', FWidth:0:1, ' x ', FHeight:0:1);
end;

function TRectangle.GetArea: Real;
begin
  Result := FWidth * FHeight;
  WriteLn('    计算面积: ', FWidth:0:1, ' * ', FHeight:0:1, ' = ', Result:0:2);
end;

function TRectangle.GetPerimeter: Real;
begin
  Result := 2 * (FWidth + FHeight);
  WriteLn('    计算周长: 2 * (', FWidth:0:1, ' + ', FHeight:0:1, ') = ', Result:0:2);
end;

function TRectangle.GetIsSquare: Boolean;
begin
  Result := FWidth = FHeight;
  WriteLn('    检查是否为正方形: ', FWidth:0:1, ' = ', FHeight:0:1, ' ? ', Result);
end;

// 定义一个银行账户类，演示带验证的属性
type
  TBankAccount = class
  private
    FAccountNumber: String;
    FBalance: Real;
    FInterestRate: Real;
    
    // 属性访问方法
    procedure SetBalance(Value: Real);
    procedure SetInterestRate(Value: Real);
    
  public
    constructor Create(const AAccountNumber: String; InitialBalance: Real);
    
    procedure Deposit(Amount: Real);
    function Withdraw(Amount: Real): Boolean;
    
    // 属性声明
    property AccountNumber: String read FAccountNumber;
    property Balance: Real read FBalance write SetBalance;
    property InterestRate: Real read FInterestRate write SetInterestRate;
  end;

// TBankAccount类的实现
constructor TBankAccount.Create(const AAccountNumber: String; InitialBalance: Real);
begin
  inherited Create;
  FAccountNumber := AAccountNumber;
  FBalance := InitialBalance;
  FInterestRate := 0.0;
  WriteLn('  创建银行账户: ', FAccountNumber, ', 初始余额: ', InitialBalance:0:2);
end;

procedure TBankAccount.SetBalance(Value: Real);
begin
  WriteLn('    尝试直接设置余额为: ', Value:0:2);
  // 通常不允许直接设置余额，这里仅作演示
  if Value >= 0 then
    FBalance := Value
  else
    WriteLn('    错误：余额不能为负数');
end;

procedure TBankAccount.SetInterestRate(Value: Real);
begin
  WriteLn('    设置利率为: ', Value:0:2, '%');
  if (Value >= 0) and (Value <= 100) then
    FInterestRate := Value
  else
    WriteLn('    错误：利率必须在0-100之间');
end;

procedure TBankAccount.Deposit(Amount: Real);
begin
  if Amount > 0 then
  begin
    FBalance := FBalance + Amount;
    WriteLn('    存款 ', Amount:0:2, ' 元，当前余额: ', FBalance:0:2, ' 元');
  end
  else
    WriteLn('    存款金额必须大于0');
end;

function TBankAccount.Withdraw(Amount: Real): Boolean;
begin
  Result := False;
  if Amount > 0 then
  begin
    if FBalance >= Amount then
    begin
      FBalance := FBalance - Amount;
      WriteLn('    取款 ', Amount:0:2, ' 元，当前余额: ', FBalance:0:2, ' 元');
      Result := True;
    end
    else
      WriteLn('    余额不足，取款失败');
  end
  else
    WriteLn('    取款金额必须大于0');
end;

// 程序主入口
begin
  WriteLn('=== Pascal 属性示例 ===');
  WriteLn;
  
  // 1. 基本属性操作
  WriteLn('1. 基本属性操作:');
  var person: TPerson := TPerson.Create('张三', 25, 'zhangsan@example.com');
  try
    WriteLn('  姓名: ', person.Name);
    WriteLn('  年龄: ', person.Age);
    WriteLn('  邮箱: ', person.Email);
    WriteLn('  ID: ', person.ID);
    
    WriteLn;
    person.Name := '李四';
    person.Age := 30;
    person.Email := 'lisi@example.com';
    
    WriteLn;
    WriteLn('  修改后的信息:');
    WriteLn('  姓名: ', person.Name);
    WriteLn('  年龄: ', person.Age);
    WriteLn('  邮箱: ', person.Email);
    
    WriteLn;
    // 测试验证逻辑
    WriteLn('  测试验证逻辑:');
    person.Name := '';
    person.Age := -5;
    person.Email := 'invalid-email';
    
    WriteLn;
    WriteLn('  验证后的信息:');
    WriteLn('  姓名: ', person.Name);
    WriteLn('  年龄: ', person.Age);
    WriteLn('  邮箱: ', person.Email);
  finally
    person.Free;
  end;
  
  WriteLn;
  
  // 2. 计算属性演示
  WriteLn('2. 计算属性演示:');
  var rectangle: TRectangle := TRectangle.Create(5.0, 3.0);
  try
    WriteLn('  矩形尺寸: ', rectangle.Width:0:1, ' x ', rectangle.Height:0:1);
    WriteLn('  面积: ', rectangle.Area:0:2);
    WriteLn('  周长: ', rectangle.Perimeter:0:2);
    WriteLn('  是正方形: ', rectangle.IsSquare);
    
    WriteLn;
    // 修改尺寸
    rectangle.Width := 4.0;
    rectangle.Height := 4.0;
    WriteLn('  修改后尺寸: ', rectangle.Width:0:1, ' x ', rectangle.Height:0:1);
    WriteLn('  面积: ', rectangle.Area:0:2);
    WriteLn('  周长: ', rectangle.Perimeter:0:2);
    WriteLn('  是正方形: ', rectangle.IsSquare);
  finally
    rectangle.Free;
  end;
  
  WriteLn;
  
  // 3. 带验证的属性演示
  WriteLn('3. 带验证的属性演示:');
  var account: TBankAccount := TBankAccount.Create('ACC123456789', 1000.0);
  try
    WriteLn('  账户号: ', account.AccountNumber);
    WriteLn('  余额: ', account.Balance:0:2);
    
    WriteLn;
    // 存款和取款
    account.Deposit(500.0);
    account.Withdraw(200.0);
    WriteLn('  当前余额: ', account.Balance:0:2);
    
    WriteLn;
    // 设置利率
    account.InterestRate := 3.5;
    WriteLn('  利率: ', account.InterestRate:0:2, '%');
    
    WriteLn;
    // 测试验证逻辑
    WriteLn('  测试验证逻辑:');
    account.Balance := -100.0;  // 尝试设置负余额
    account.InterestRate := 150.0;  // 尝试设置过高利率
  finally
    account.Free;
  end;
  
  WriteLn;
  
  // 4. 属性与继承
  WriteLn('4. 属性与继承:');
  
  // 创建派生类来演示属性继承
  type
    TEmployee = class(TPerson)
    private
      FSalary: Real;
      function GetSalary: Real;
      procedure SetSalary(Value: Real);
    public
      constructor Create(const AName: String; AAge: Integer; const AEmail: String; ASalary: Real);
      property Salary: Real read GetSalary write SetSalary;
    end;
  
  constructor TEmployee.Create(const AName: String; AAge: Integer; const AEmail: String; ASalary: Real);
  begin
    inherited Create(AName, AAge, AEmail);
    FSalary := ASalary;
    WriteLn('  创建员工: ', AName, ', 薪资: ', ASalary:0:2);
  end;
  
  function TEmployee.GetSalary: Real;
  begin
    WriteLn('    读取薪资属性');
    Result := FSalary;
  end;
  
  procedure TEmployee.SetSalary(Value: Real);
  begin
    WriteLn('    设置薪资属性为: ', Value:0:2);
    if Value >= 0 then
      FSalary := Value
    else
      FSalary := 0;
  end;
  
  var employee: TEmployee := TEmployee.Create('王五', 28, 'wangwu@company.com', 8000.0);
  try
    WriteLn('  员工信息:');
    // 继承的属性
    WriteLn('    姓名: ', employee.Name);
    WriteLn('    年龄: ', employee.Age);
    WriteLn('    邮箱: ', employee.Email);
    WriteLn('    ID: ', employee.ID);
    
    // 自己的属性
    WriteLn('    薪资: ', employee.Salary:0:2);
    
    WriteLn;
    // 修改属性
    employee.Name := '王小五';
    employee.Salary := 9000.0;
    
    WriteLn('  修改后信息:');
    WriteLn('    姓名: ', employee.Name);
    WriteLn('    薪资: ', employee.Salary:0:2);
  finally
    employee.Free;
  end;
  
  WriteLn;
  WriteLn('演示了 Pascal 中属性的各种用法');
  
  // 等待用户按键，防止程序窗口立即关闭
  ReadLn;
end.